﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using devtm.Collections;
using System.IO;
using System.Reflection;
using System.Diagnostics;
using devtm.Application.License;

namespace devtm.Types.LazyLoading
{
    public class ListeAssemblies : TableCollection<AssemblyNamespace, string>
    {

        public event EventHandler<ListeAssembliesEventArgs> Events;

        static ListeAssemblies _current = null;

        private ResolveAssembly Resolver;
        private bool _isDisposed;

        public ListeAssemblies()
            : base(c => c.Filename)
        {

            LoadByDefault = new TableCollection<AssemblyDescription, string>(c => c._toString);
            if (_current != null && !_isDisposed)
                throw new InvalidOperationException("UnitofWorkScope instances cannot be nested.");


            Resolver = new ResolveAssembly();
            Types = new TableCollection<DescType, String>(c => c.Key);
            Assemblies = new TableCollection<Assembly, String>(c => c.FullName);


            _current = this;
            _isDisposed = false;
        }


        public void Initialize()
        {
            foreach (AssemblyNamespace item in this)
                if (item.MustLoad && !item.Initialized && !item.IsInError)
                    item.Initialize();

            if (Events != null)
                Events(this, new ListeAssembliesEventArgs() { MethodName = "Initialize" });

        }

        public void LoadTypes()
        {
            foreach (AssemblyNamespace item in this)     
                if (item.MustLoad && item.Initialized && !item.IsInError)
                    item.LoadTypes();

            if (Events != null)
                Events(this, new ListeAssembliesEventArgs() { MethodName = "LoadTypes" });

        }


        /// <summary>
        /// Gets the current. ListeAssemblies
        /// </summary>
        /// <value>The current.</value>
        static public  ListeAssemblies Current
        {
            get
            {
                return _current;
            }
        }


        public TableCollection<DescType, String> Types { get; set; }
        public TableCollection<Assembly, String> Assemblies { get; set; }


        public void AddDirectorySearch(System.IO.DirectoryInfo dir)
        {

            foreach (FileInfo file in dir.EnumerateFiles("*.dll", SearchOption.AllDirectories))
                CreateAssemblyFilename(file);

            if (Events != null)
                Events(this, new ListeAssembliesEventArgs() { MethodName = "AddDirectorySearch" });

        }


        public AssemblyNamespace AddFileAssembly(Assembly ass)
        {

            var ii = new AssemblyFileNamespace(new FileInfo(ass.Location));
            ii.AssemblyLoadEventHandler += ii_AssemblyLoadEventHandler;

            if (this.ContainsKeyFrom(ii))
                return this[ii.Filename];

            ii.Initialize(ass);

            foreach (AssemblyDescription i in ii.References)
                foreach (AssemblyDescription j in LoadByDefault)
                    if (j.Equals(i))
                    {
                        ii.MustLoad = true;
                        break;
                    }

            if (!ii.Hide && !this.ContainsKeyFrom(ii))
            {
                this.Add(ii);
                ii.CollectionChanged += ii_CollectionChanged;
            }

            return ii;

        }

        void ii_AssemblyLoadEventHandler(object sender, AssemblyLoadEventArgs e)
        {

            if (!Assemblies.ContainsKeyFrom(e.LoadedAssembly))
                Assemblies.Add(e.LoadedAssembly);

        }


        public void CreateAssemblyFilename(FileInfo file)
        {

            uint crc = 0;

            foreach (AssemblyNamespace item in this)
                if (item is AssemblyFileNamespace)
                {
                    if (file.FullName == item.Filename)
                        return;

                    else if (file.Name == Path.GetFileName(item.Filename))
                    {
                        if (crc == 0)
                            crc = GetCrc(file, crc);

                        if (item.Crc == crc)
                            return;
                    }
                }

            var ii = new AssemblyFileNamespace(file);
            ii.Crc = crc;
            ii.AssemblyLoadEventHandler += ii_AssemblyLoadEventHandler;

            if (this.ContainsKeyFrom(ii))
                return;

            foreach (AssemblyDescription i in ii.References)
                foreach (AssemblyDescription j in LoadByDefault)
                    if (j.Equals(i))
                    {
                        ii.MustLoad = true;
                        break;
                    }

            if (!ii.Hide && !this.ContainsKeyFrom(ii))
            {
                this.Add(ii);
                ii.CollectionChanged += ii_CollectionChanged;
            }

        }

        private static uint GetCrc(FileInfo file, uint crc)
        {


            try
            {

                using (FileStream f = new FileStream(file.FullName, FileMode.Open))
                    crc = Crc32.Calculate(f);
                return crc;

            }
            catch (Exception)
            {
                return 0;                
            }

        }


        public AssemblyNamespace AddAssemblyDescription(AssemblyDescription item)
        {

            if (item.processorArchitecture != "x86")
                return null;

            Debug.WriteLine(item.AssemblyName);
            var v = new AssemblyGacNamespace(item);
            v.AssemblyLoadEventHandler += ii_AssemblyLoadEventHandler;

            if (this.ContainsKeyFrom(v))
                return this[v.Filename];

            foreach (AssemblyDescription i in v.References)
                foreach (AssemblyDescription j in LoadByDefault)
                    if (j.Equals(i))
                    {
                        v.MustLoad = true;
                        break;
                    }

            v.Initialize();

            v.CollectionChanged += ii_CollectionChanged;
        
            this.Add(v);

            return v;

        }

        public AssemblyNamespace AddGacAssembly(Assembly assembly)
        {

            AssemblyDescription item = new AssemblyDescription(assembly.FullName);

            if (item.processorArchitecture != "x86")
                return null;

            Debug.WriteLine(item.AssemblyName);
            var v = new AssemblyGacNamespace(item);
            v.AssemblyLoadEventHandler += ii_AssemblyLoadEventHandler;

            if (this.ContainsKeyFrom(v))
                return this[v.Filename];

            foreach (AssemblyDescription i in v.References)
                foreach (AssemblyDescription j in LoadByDefault)
                    if (j.Equals(i))
                    {
                        v.MustLoad = true;
                        break;
                    }

            v.Initialize(assembly);

            v.CollectionChanged += ii_CollectionChanged;

            this.Add(v);

            return v;

        }


        void ii_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {

            if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)

                foreach (Type item in e.NewItems)
                    if (!this.Types.ContainsKey(item.AssemblyQualifiedName))
                        this.Types.Add(new DescType(item,(AssemblyNamespace)sender));

        }

        
        #region IDisposable Members

       protected virtual void Dispose(bool disposing)
        {

            if (disposing)
            {


            }

            if (!_isDisposed)
            {
                _current = null;
                _isDisposed = true;
             }
            
        }

        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }


        ~ListeAssemblies()
        {
            Dispose(false);
        }
        
        #endregion


        public TableCollection<AssemblyDescription, string> LoadByDefault { get; set; }

    }


    public class ListeAssembliesEventArgs : EventArgs
    {
        public string MethodName { get; set; }
    }

}
